{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Use OffPAC to Play Acrobot-v1\n",
    "\n",
    "TensorFlow version"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "\n",
    "import sys\n",
    "import logging\n",
    "import itertools\n",
    "\n",
    "import numpy as np\n",
    "np.random.seed(0)\n",
    "import pandas as pd\n",
    "import gym\n",
    "import matplotlib.pyplot as plt\n",
    "import tensorflow.compat.v2 as tf\n",
    "tf.random.set_seed(0)\n",
    "from tensorflow import keras\n",
    "from tensorflow import nn\n",
    "from tensorflow import optimizers\n",
    "from tensorflow.keras import layers\n",
    "from tensorflow.keras import losses\n",
    "\n",
    "logging.basicConfig(level=logging.DEBUG,\n",
    "        format='%(asctime)s [%(levelname)s] %(message)s',\n",
    "        stream=sys.stdout, datefmt='%H:%M:%S')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "00:00:00 [INFO] env: <AcrobotEnv<Acrobot-v1>>\n",
      "00:00:00 [INFO] action_space: Discrete(3)\n",
      "00:00:00 [INFO] observation_space: Box(-28.274333953857422, 28.274333953857422, (6,), float32)\n",
      "00:00:00 [INFO] reward_range: (-inf, inf)\n",
      "00:00:00 [INFO] metadata: {'render.modes': ['human', 'rgb_array'], 'video.frames_per_second': 15}\n",
      "00:00:00 [INFO] _max_episode_steps: 500\n",
      "00:00:00 [INFO] _elapsed_steps: None\n",
      "00:00:00 [INFO] id: Acrobot-v1\n",
      "00:00:00 [INFO] entry_point: gym.envs.classic_control:AcrobotEnv\n",
      "00:00:00 [INFO] reward_threshold: -100.0\n",
      "00:00:00 [INFO] nondeterministic: False\n",
      "00:00:00 [INFO] max_episode_steps: 500\n",
      "00:00:00 [INFO] _kwargs: {}\n",
      "00:00:00 [INFO] _env_name: Acrobot\n"
     ]
    }
   ],
   "source": [
    "env = gym.make('Acrobot-v1')\n",
    "env.seed(0)\n",
    "for key in vars(env):\n",
    "    logging.info('%s: %s', key, vars(env)[key])\n",
    "for key in vars(env.spec):\n",
    "    logging.info('%s: %s', key, vars(env.spec)[key])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class OffPACAgent:\n",
    "    def __init__(self, env):\n",
    "        self.action_n = env.action_space.n\n",
    "        self.gamma = 0.99\n",
    "\n",
    "        self.actor_net = self.build_net(hidden_sizes=[100,],\n",
    "                output_size=self.action_n,\n",
    "                output_activation=nn.softmax, learning_rate=0.0001)\n",
    "        self.critic_net = self.build_net(hidden_sizes=[100,],\n",
    "                output_size=self.action_n,\n",
    "                learning_rate=0.0002)\n",
    "\n",
    "    def build_net(self, hidden_sizes, output_size,\n",
    "                activation=nn.relu, output_activation=None,\n",
    "                loss=losses.mse, learning_rate=0.001):\n",
    "        model = keras.Sequential()\n",
    "        for hidden_size in hidden_sizes:\n",
    "            model.add(layers.Dense(units=hidden_size,\n",
    "                    activation=activation))\n",
    "        model.add(layers.Dense(units=output_size,\n",
    "                activation=output_activation))\n",
    "        optimizer = optimizers.SGD(learning_rate)\n",
    "        model.compile(optimizer=optimizer, loss=loss)\n",
    "        return model\n",
    "\n",
    "    def reset(self, mode=None):\n",
    "        self.mode = mode\n",
    "        if self.mode == 'train':\n",
    "            self.trajectory = []\n",
    "            self.discount = 1.\n",
    "\n",
    "    def step(self, observation, reward, done):\n",
    "        if self.mode == 'train':\n",
    "            action = np.random.choice(self.action_n)\n",
    "            self.trajectory += [observation, reward, done, action]\n",
    "            if len(self.trajectory) >= 8:\n",
    "                self.learn()\n",
    "            self.discount *= self.gamma\n",
    "        else:\n",
    "            probs = self.actor_net.predict(observation[np.newaxis])[0]\n",
    "            action = np.random.choice(self.action_n, p=probs)\n",
    "        return action\n",
    "\n",
    "    def close(self):\n",
    "        pass\n",
    "\n",
    "    def learn(self):\n",
    "        state, _, _, action, next_state, reward, done, next_action = \\\n",
    "                self.trajectory[-8:]\n",
    "        behavior_prob = 1. / self.action_n\n",
    "        pi = self.actor_net.predict(state[np.newaxis])[0, action]\n",
    "        ratio = pi / behavior_prob # importance sampling ratio\n",
    "\n",
    "        # train actor\n",
    "        q = self.critic_net.predict(state[np.newaxis])[0, action]\n",
    "        state_tensor = tf.convert_to_tensor(state[np.newaxis], dtype=tf.float32)\n",
    "        with tf.GradientTape() as tape:\n",
    "            pi_tensor = self.actor_net(state_tensor)[0, action]\n",
    "            actor_loss_tensor = -self.discount * q / behavior_prob * pi_tensor\n",
    "        grad_tensors = tape.gradient(actor_loss_tensor, self.actor_net.variables)\n",
    "        self.actor_net.optimizer.apply_gradients(zip(grad_tensors,\n",
    "                self.actor_net.variables))\n",
    "\n",
    "        # train critic\n",
    "        next_q = self.critic_net.predict(next_state[np.newaxis])[0, next_action]\n",
    "        target = reward + (1. - done) * self.gamma * next_q\n",
    "        target_tensor = tf.convert_to_tensor(target, dtype=tf.float32)\n",
    "        with tf.GradientTape() as tape:\n",
    "            q_tensor = self.critic_net(state_tensor)[:, action]\n",
    "            mse_tensor = losses.MSE(target_tensor, q_tensor)\n",
    "            critic_loss_tensor = ratio * mse_tensor\n",
    "        grad_tensors = tape.gradient(critic_loss_tensor, self.critic_net.variables)\n",
    "        self.critic_net.optimizer.apply_gradients(zip(grad_tensors,\n",
    "                self.critic_net.variables))\n",
    "\n",
    "\n",
    "agent = OffPACAgent(env)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "00:00:02 [INFO] ==== train ====\n",
      "00:03:32 [DEBUG] train episode 0: reward = -500.00, steps = 500\n",
      "00:06:54 [DEBUG] train episode 1: reward = -477.00, steps = 478\n",
      "00:10:16 [DEBUG] train episode 2: reward = -500.00, steps = 500\n",
      "00:13:26 [DEBUG] train episode 3: reward = -385.00, steps = 386\n",
      "00:15:29 [DEBUG] train episode 4: reward = -500.00, steps = 500\n",
      "00:18:32 [DEBUG] train episode 5: reward = -372.00, steps = 373\n",
      "00:21:01 [DEBUG] train episode 6: reward = -500.00, steps = 500\n",
      "00:25:18 [DEBUG] train episode 7: reward = -500.00, steps = 500\n",
      "00:29:34 [DEBUG] train episode 8: reward = -451.00, steps = 452\n",
      "00:32:04 [DEBUG] train episode 9: reward = -500.00, steps = 500\n",
      "00:35:42 [DEBUG] train episode 10: reward = -500.00, steps = 500\n",
      "00:39:15 [DEBUG] train episode 11: reward = -500.00, steps = 500\n",
      "00:42:51 [DEBUG] train episode 12: reward = -500.00, steps = 500\n",
      "00:46:28 [DEBUG] train episode 13: reward = -500.00, steps = 500\n",
      "00:49:57 [DEBUG] train episode 14: reward = -500.00, steps = 500\n",
      "00:53:27 [DEBUG] train episode 15: reward = -500.00, steps = 500\n",
      "00:56:37 [DEBUG] train episode 16: reward = -320.00, steps = 321\n",
      "01:00:03 [DEBUG] train episode 17: reward = -500.00, steps = 500\n",
      "01:03:36 [DEBUG] train episode 18: reward = -500.00, steps = 500\n",
      "01:07:02 [DEBUG] train episode 19: reward = -500.00, steps = 500\n",
      "01:10:11 [DEBUG] train episode 20: reward = -306.00, steps = 307\n",
      "01:13:38 [DEBUG] train episode 21: reward = -500.00, steps = 500\n",
      "01:16:50 [DEBUG] train episode 22: reward = -345.00, steps = 346\n",
      "01:19:59 [DEBUG] train episode 23: reward = -310.00, steps = 311\n",
      "01:23:11 [DEBUG] train episode 24: reward = -348.00, steps = 349\n",
      "01:56:15 [DEBUG] train episode 25: reward = -500.00, steps = 500\n",
      "01:59:19 [DEBUG] train episode 26: reward = -270.00, steps = 271\n",
      "02:02:31 [DEBUG] train episode 27: reward = -341.00, steps = 342\n",
      "02:04:32 [DEBUG] train episode 28: reward = -208.00, steps = 209\n",
      "03:01:29 [DEBUG] train episode 29: reward = -158.00, steps = 159\n",
      "03:04:31 [DEBUG] train episode 30: reward = -177.00, steps = 178\n",
      "03:07:28 [DEBUG] train episode 31: reward = -183.00, steps = 184\n",
      "03:10:32 [DEBUG] train episode 32: reward = -193.00, steps = 194\n",
      "03:13:36 [DEBUG] train episode 33: reward = -219.00, steps = 220\n",
      "03:16:32 [DEBUG] train episode 34: reward = -133.00, steps = 134\n",
      "03:19:38 [DEBUG] train episode 35: reward = -192.00, steps = 193\n",
      "03:22:32 [DEBUG] train episode 36: reward = -138.00, steps = 139\n",
      "03:25:22 [DEBUG] train episode 37: reward = -130.00, steps = 131\n",
      "03:28:16 [DEBUG] train episode 38: reward = -147.00, steps = 148\n",
      "03:31:14 [DEBUG] train episode 39: reward = -182.00, steps = 183\n",
      "03:34:11 [DEBUG] train episode 40: reward = -166.00, steps = 167\n",
      "03:37:40 [DEBUG] train episode 41: reward = -500.00, steps = 500\n",
      "03:40:41 [DEBUG] train episode 42: reward = -202.00, steps = 203\n",
      "03:43:44 [DEBUG] train episode 43: reward = -223.00, steps = 224\n",
      "03:46:39 [DEBUG] train episode 44: reward = -160.00, steps = 161\n",
      "03:49:36 [DEBUG] train episode 45: reward = -168.00, steps = 169\n",
      "03:52:33 [DEBUG] train episode 46: reward = -153.00, steps = 154\n",
      "03:55:33 [DEBUG] train episode 47: reward = -188.00, steps = 189\n",
      "03:58:33 [DEBUG] train episode 48: reward = -164.00, steps = 165\n",
      "04:01:37 [DEBUG] train episode 49: reward = -183.00, steps = 184\n",
      "04:04:37 [DEBUG] train episode 50: reward = -162.00, steps = 163\n",
      "04:07:46 [DEBUG] train episode 51: reward = -251.00, steps = 252\n",
      "04:10:47 [DEBUG] train episode 52: reward = -162.00, steps = 163\n",
      "04:13:44 [DEBUG] train episode 53: reward = -130.00, steps = 131\n",
      "04:16:44 [DEBUG] train episode 54: reward = -153.00, steps = 154\n",
      "04:19:44 [DEBUG] train episode 55: reward = -175.00, steps = 176\n",
      "04:22:48 [DEBUG] train episode 56: reward = -176.00, steps = 177\n",
      "04:25:50 [DEBUG] train episode 57: reward = -148.00, steps = 149\n",
      "04:28:54 [DEBUG] train episode 58: reward = -151.00, steps = 152\n",
      "04:31:55 [DEBUG] train episode 59: reward = -154.00, steps = 155\n",
      "04:34:55 [DEBUG] train episode 60: reward = -158.00, steps = 159\n",
      "04:37:56 [DEBUG] train episode 61: reward = -157.00, steps = 158\n",
      "04:41:00 [DEBUG] train episode 62: reward = -162.00, steps = 163\n",
      "04:44:01 [DEBUG] train episode 63: reward = -178.00, steps = 179\n",
      "04:47:03 [DEBUG] train episode 64: reward = -144.00, steps = 145\n",
      "04:50:14 [DEBUG] train episode 65: reward = -232.00, steps = 233\n",
      "04:53:12 [DEBUG] train episode 66: reward = -142.00, steps = 143\n",
      "04:56:17 [DEBUG] train episode 67: reward = -157.00, steps = 158\n",
      "04:59:19 [DEBUG] train episode 68: reward = -133.00, steps = 134\n",
      "05:02:20 [DEBUG] train episode 69: reward = -200.00, steps = 201\n",
      "05:05:13 [DEBUG] train episode 70: reward = -137.00, steps = 138\n",
      "05:08:05 [DEBUG] train episode 71: reward = -137.00, steps = 138\n",
      "05:10:57 [DEBUG] train episode 72: reward = -126.00, steps = 127\n",
      "05:13:50 [DEBUG] train episode 73: reward = -131.00, steps = 132\n",
      "05:16:43 [DEBUG] train episode 74: reward = -136.00, steps = 137\n",
      "05:19:33 [DEBUG] train episode 75: reward = -122.00, steps = 123\n",
      "05:22:26 [DEBUG] train episode 76: reward = -143.00, steps = 144\n",
      "05:25:20 [DEBUG] train episode 77: reward = -159.00, steps = 160\n",
      "05:28:15 [DEBUG] train episode 78: reward = -152.00, steps = 153\n",
      "05:31:09 [DEBUG] train episode 79: reward = -154.00, steps = 155\n",
      "05:31:09 [INFO] ==== test ====\n",
      "05:31:25 [DEBUG] test episode 0: reward = -160.00, steps = 161\n",
      "05:31:44 [DEBUG] test episode 1: reward = -186.00, steps = 187\n",
      "05:32:02 [DEBUG] test episode 2: reward = -175.00, steps = 176\n",
      "05:32:17 [DEBUG] test episode 3: reward = -146.00, steps = 147\n",
      "05:32:32 [DEBUG] test episode 4: reward = -147.00, steps = 148\n",
      "05:32:48 [DEBUG] test episode 5: reward = -152.00, steps = 153\n",
      "05:33:05 [DEBUG] test episode 6: reward = -170.00, steps = 171\n",
      "05:33:19 [DEBUG] test episode 7: reward = -129.00, steps = 130\n",
      "05:33:35 [DEBUG] test episode 8: reward = -155.00, steps = 156\n",
      "05:33:51 [DEBUG] test episode 9: reward = -156.00, steps = 157\n",
      "05:34:04 [DEBUG] test episode 10: reward = -124.00, steps = 125\n",
      "05:34:16 [DEBUG] test episode 11: reward = -119.00, steps = 120\n",
      "05:34:33 [DEBUG] test episode 12: reward = -170.00, steps = 171\n",
      "05:34:49 [DEBUG] test episode 13: reward = -151.00, steps = 152\n",
      "05:35:02 [DEBUG] test episode 14: reward = -129.00, steps = 130\n",
      "05:35:18 [DEBUG] test episode 15: reward = -156.00, steps = 157\n",
      "05:35:32 [DEBUG] test episode 16: reward = -133.00, steps = 134\n",
      "05:35:50 [DEBUG] test episode 17: reward = -171.00, steps = 172\n",
      "05:36:06 [DEBUG] test episode 18: reward = -161.00, steps = 162\n",
      "05:36:21 [DEBUG] test episode 19: reward = -138.00, steps = 139\n",
      "05:36:35 [DEBUG] test episode 20: reward = -139.00, steps = 140\n",
      "05:36:50 [DEBUG] test episode 21: reward = -149.00, steps = 150\n",
      "05:37:11 [DEBUG] test episode 22: reward = -206.00, steps = 207\n",
      "05:37:29 [DEBUG] test episode 23: reward = -173.00, steps = 174\n",
      "05:37:44 [DEBUG] test episode 24: reward = -144.00, steps = 145\n",
      "05:38:01 [DEBUG] test episode 25: reward = -158.00, steps = 159\n",
      "05:38:15 [DEBUG] test episode 26: reward = -140.00, steps = 141\n",
      "05:38:31 [DEBUG] test episode 27: reward = -149.00, steps = 150\n",
      "05:38:43 [DEBUG] test episode 28: reward = -119.00, steps = 120\n",
      "05:38:56 [DEBUG] test episode 29: reward = -124.00, steps = 125\n",
      "05:39:11 [DEBUG] test episode 30: reward = -143.00, steps = 144\n",
      "05:39:28 [DEBUG] test episode 31: reward = -168.00, steps = 169\n",
      "05:39:51 [DEBUG] test episode 32: reward = -214.00, steps = 215\n",
      "05:40:06 [DEBUG] test episode 33: reward = -150.00, steps = 151\n",
      "05:40:23 [DEBUG] test episode 34: reward = -167.00, steps = 168\n",
      "05:40:38 [DEBUG] test episode 35: reward = -142.00, steps = 143\n",
      "05:40:51 [DEBUG] test episode 36: reward = -129.00, steps = 130\n",
      "05:41:05 [DEBUG] test episode 37: reward = -139.00, steps = 140\n",
      "05:41:18 [DEBUG] test episode 38: reward = -118.00, steps = 119\n",
      "05:41:33 [DEBUG] test episode 39: reward = -151.00, steps = 152\n",
      "05:41:51 [DEBUG] test episode 40: reward = -173.00, steps = 174\n",
      "05:42:08 [DEBUG] test episode 41: reward = -171.00, steps = 172\n",
      "05:42:22 [DEBUG] test episode 42: reward = -129.00, steps = 130\n",
      "05:42:34 [DEBUG] test episode 43: reward = -123.00, steps = 124\n",
      "05:42:50 [DEBUG] test episode 44: reward = -151.00, steps = 152\n",
      "05:43:09 [DEBUG] test episode 45: reward = -182.00, steps = 183\n",
      "05:43:24 [DEBUG] test episode 46: reward = -142.00, steps = 143\n",
      "05:43:39 [DEBUG] test episode 47: reward = -153.00, steps = 154\n",
      "05:43:55 [DEBUG] test episode 48: reward = -149.00, steps = 150\n",
      "05:44:28 [DEBUG] test episode 49: reward = -325.00, steps = 326\n",
      "05:44:44 [DEBUG] test episode 50: reward = -151.00, steps = 152\n",
      "05:45:05 [DEBUG] test episode 51: reward = -205.00, steps = 206\n",
      "05:45:22 [DEBUG] test episode 52: reward = -163.00, steps = 164\n",
      "05:45:38 [DEBUG] test episode 53: reward = -155.00, steps = 156\n",
      "05:45:55 [DEBUG] test episode 54: reward = -167.00, steps = 168\n",
      "05:46:10 [DEBUG] test episode 55: reward = -147.00, steps = 148\n",
      "05:46:26 [DEBUG] test episode 56: reward = -158.00, steps = 159\n",
      "05:46:42 [DEBUG] test episode 57: reward = -147.00, steps = 148\n",
      "05:47:02 [DEBUG] test episode 58: reward = -200.00, steps = 201\n",
      "05:47:18 [DEBUG] test episode 59: reward = -159.00, steps = 160\n",
      "05:47:38 [DEBUG] test episode 60: reward = -195.00, steps = 196\n",
      "05:47:59 [DEBUG] test episode 61: reward = -203.00, steps = 204\n",
      "05:48:14 [DEBUG] test episode 62: reward = -147.00, steps = 148\n",
      "05:48:30 [DEBUG] test episode 63: reward = -156.00, steps = 157\n",
      "05:48:52 [DEBUG] test episode 64: reward = -207.00, steps = 208\n",
      "05:49:08 [DEBUG] test episode 65: reward = -161.00, steps = 162\n",
      "05:49:26 [DEBUG] test episode 66: reward = -175.00, steps = 176\n",
      "05:49:44 [DEBUG] test episode 67: reward = -172.00, steps = 173\n",
      "05:50:04 [DEBUG] test episode 68: reward = -197.00, steps = 198\n",
      "05:50:20 [DEBUG] test episode 69: reward = -152.00, steps = 153\n",
      "05:50:34 [DEBUG] test episode 70: reward = -148.00, steps = 149\n",
      "05:50:49 [DEBUG] test episode 71: reward = -142.00, steps = 143\n",
      "05:51:06 [DEBUG] test episode 72: reward = -159.00, steps = 160\n",
      "05:51:22 [DEBUG] test episode 73: reward = -157.00, steps = 158\n",
      "05:51:41 [DEBUG] test episode 74: reward = -192.00, steps = 193\n",
      "05:51:59 [DEBUG] test episode 75: reward = -167.00, steps = 168\n",
      "05:52:15 [DEBUG] test episode 76: reward = -164.00, steps = 165\n",
      "05:52:32 [DEBUG] test episode 77: reward = -162.00, steps = 163\n",
      "05:52:51 [DEBUG] test episode 78: reward = -187.00, steps = 188\n",
      "05:53:06 [DEBUG] test episode 79: reward = -142.00, steps = 143\n",
      "05:53:25 [DEBUG] test episode 80: reward = -189.00, steps = 190\n",
      "05:53:38 [DEBUG] test episode 81: reward = -130.00, steps = 131\n",
      "05:53:53 [DEBUG] test episode 82: reward = -148.00, steps = 149\n",
      "05:54:19 [DEBUG] test episode 83: reward = -244.00, steps = 245\n",
      "05:54:35 [DEBUG] test episode 84: reward = -158.00, steps = 159\n",
      "05:54:50 [DEBUG] test episode 85: reward = -154.00, steps = 155\n",
      "05:55:06 [DEBUG] test episode 86: reward = -152.00, steps = 153\n",
      "05:55:19 [DEBUG] test episode 87: reward = -128.00, steps = 129\n",
      "05:55:50 [DEBUG] test episode 88: reward = -300.00, steps = 301\n",
      "05:56:11 [DEBUG] test episode 89: reward = -213.00, steps = 214\n",
      "05:56:27 [DEBUG] test episode 90: reward = -146.00, steps = 147\n",
      "05:56:43 [DEBUG] test episode 91: reward = -154.00, steps = 155\n",
      "05:57:04 [DEBUG] test episode 92: reward = -207.00, steps = 208\n",
      "05:57:23 [DEBUG] test episode 93: reward = -188.00, steps = 189\n",
      "05:57:39 [DEBUG] test episode 94: reward = -155.00, steps = 156\n",
      "05:58:03 [DEBUG] test episode 95: reward = -234.00, steps = 235\n",
      "05:58:16 [DEBUG] test episode 96: reward = -132.00, steps = 133\n",
      "05:58:33 [DEBUG] test episode 97: reward = -160.00, steps = 161\n",
      "05:58:50 [DEBUG] test episode 98: reward = -168.00, steps = 169\n",
      "05:59:04 [DEBUG] test episode 99: reward = -140.00, steps = 141\n",
      "05:59:04 [INFO] average episode reward = -162.85 ± 32.87\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "def play_episode(env, agent, max_episode_steps=None, mode=None, render=False):\n",
    "    observation, reward, done = env.reset(), 0., False\n",
    "    agent.reset(mode=mode)\n",
    "    episode_reward, elapsed_steps = 0., 0\n",
    "    while True:\n",
    "        action = agent.step(observation, reward, done)\n",
    "        if render:\n",
    "            env.render()\n",
    "        if done:\n",
    "            break\n",
    "        observation, reward, done, _ = env.step(action)\n",
    "        episode_reward += reward\n",
    "        elapsed_steps += 1\n",
    "        if max_episode_steps and elapsed_steps >= max_episode_steps:\n",
    "            break\n",
    "    agent.close()\n",
    "    return episode_reward, elapsed_steps\n",
    "\n",
    "\n",
    "logging.info('==== train ====')\n",
    "episode_rewards = []\n",
    "for episode in itertools.count():\n",
    "    play_episode(env.unwrapped, agent,\n",
    "            max_episode_steps=env._max_episode_steps, mode='train')\n",
    "    episode_reward, elapsed_steps = play_episode(env, agent)\n",
    "    episode_rewards.append(episode_reward)\n",
    "    logging.debug('train episode %d: reward = %.2f, steps = %d',\n",
    "            episode, episode_reward, elapsed_steps)\n",
    "    if np.mean(episode_rewards[-10:]) > -140:\n",
    "        break\n",
    "plt.plot(episode_rewards)\n",
    "\n",
    "\n",
    "logging.info('==== test ====')\n",
    "episode_rewards = []\n",
    "for episode in range(100):\n",
    "    episode_reward, elapsed_steps = play_episode(env, agent)\n",
    "    episode_rewards.append(episode_reward)\n",
    "    logging.debug('test episode %d: reward = %.2f, steps = %d',\n",
    "            episode, episode_reward, elapsed_steps)\n",
    "logging.info('average episode reward = %.2f ± %.2f',\n",
    "        np.mean(episode_rewards), np.std(episode_rewards))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "env.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
